package org.example.learn.asm.update;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.AdviceAdapter;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class UpdateOriginMethod extends ClassLoader {

    public static void main(String[] args) throws IOException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {

        // 读取原有类型
        ClassReader cr = new ClassReader(Origin.class.getName());
        // 开始增强字节码
        ClassWriter cw = new ClassWriter(cr,ClassWriter.COMPUTE_MAXS);

        {
            MethodVisitor methodVisitor = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
            methodVisitor.visitVarInsn(Opcodes.ALOAD,0);
            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL,"java/lang/Object","<init>","()V",false);
            methodVisitor.visitInsn(Opcodes.RETURN);
            methodVisitor.visitMaxs(1,1);
            methodVisitor.visitEnd();
        }

        ClassVisitor cv = new ProfilingClassAdapter(cw,Origin.class.getSimpleName());
        cr.accept(cv,ClassReader.EXPAND_FRAMES);

        byte[] bytes = cw.toByteArray();
//        outputClazz(bytes); // 将编辑后字节输出为class文件

        // 使用反射执行 编辑后的字节码
        Class<?> clazz = new UpdateOriginMethod().defineClass("org.example.learn.asm.update.Origin",bytes,0,bytes.length);
        Method method = clazz.getMethod("OriginMethod",String.class);
        Object var = method.invoke(clazz.newInstance(),"112");

        System.out.println(var);
    }

    private static void outputClazz(byte[] bytes){
        FileOutputStream out = null;
        String path = UpdateOriginMethod.class.getResource("/").getPath() + "OriginMethodUpdate.class";

        try {
            out = new FileOutputStream(path);
            System.out.println("输出路径：" + path);
            out.write(bytes);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (null != out){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    static class ProfilingClassAdapter extends ClassVisitor{

        public ProfilingClassAdapter(final ClassVisitor cv,String innerClassName){
            super(Opcodes.ASM5,cv);
        }

        public MethodVisitor visitMethod(int access,String name,String desc,String signature,String[] exceptions){
            System.out.println("access: " + access);
            System.out.println("name: " + name);
            System.out.println("desc: " + desc);

            if (!"OriginMethod".equals(name)) return null;

            MethodVisitor mv = cv.visitMethod(access,name,desc,signature,exceptions);

            return new ProfilingMethodVisitor(mv,access,name,desc);
        }

        static class ProfilingMethodVisitor extends AdviceAdapter {

            private String methodName = "";

            public ProfilingMethodVisitor(MethodVisitor mv, int access, String name, String desc) {
                super(Opcodes.ASM5,mv,access,name,desc);
                this.methodName = name;
            }

            // 方法进入事件处理逻辑
            @Override
            protected void onMethodEnter() {
                mv.visitMethodInsn(INVOKESTATIC,"java/lang/System","nanoTime","()J",false);
                mv.visitVarInsn(LSTORE,2);
                mv.visitVarInsn(ALOAD,1);
//                super.onMethodEnter();
            }

            // 方法退出时的处理逻辑
            @Override
            protected void onMethodExit(int opcode) {
                // 添加执行耗时时间代码
                if ((IRETURN <= opcode && opcode <= RETURN) || opcode == ATHROW){
                   mv.visitFieldInsn(GETSTATIC,"java/lang/System","out","Ljava/io/PrintStream;");

                   mv.visitTypeInsn(NEW,"java/lang/StringBuilder");
                   mv.visitInsn(DUP);
                   mv.visitMethodInsn(INVOKESPECIAL,"java/lang/StringBuilder","<init>","()V",false);
                   mv.visitLdcInsn("方法执行耗时（纳秒）→ →" + methodName + ":");
                   mv.visitMethodInsn(INVOKEVIRTUAL,"java/lang/StringBuilder","append","(Ljava/lang/String;)Ljava/lang/StringBuilder;",false);

                   // 添加计算耗时时间代码
                   mv.visitMethodInsn(INVOKESTATIC,"java/lang/System","nanoTime","()J",false);
                   mv.visitVarInsn(LLOAD,2);
                   mv.visitInsn(LSUB);

                   mv.visitMethodInsn(INVOKEVIRTUAL,"java/lang/StringBuilder","append","(J)Ljava/lang/StringBuilder;",false);
                   mv.visitMethodInsn(INVOKEVIRTUAL,"java/lang/StringBuilder","toString","()Ljava/lang/String;",false);
                   mv.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println","(Ljava/lang/String;)V",false);
                }
            }
        }
    }

}
